home *** CD-ROM | disk | FTP | other *** search
/ TPUG - Toronto PET Users Group / TPUG Users Group CD / TPUG Users Group CD.iso / AMIGA / AMICUS / AMICUS02.ADF / Emacs / display.c < prev    next >
C/C++ Source or Header  |  1989-05-30  |  22KB  |  862 lines

  1. /* display.c */
  2.  
  3. /*
  4.  * The functions in this file handle redisplay. There are two halves, the
  5.  * ones that update the virtual display screen, and the ones that make the
  6.  * physical display screen the same as the virtual display screen. These
  7.  * functions use hints that are left in the windows by the commands.
  8.  *
  9.  * REVISION HISTORY:
  10.  *
  11.  * ?    Steve Wilhite, 1-Dec-85
  12.  *      - massive cleanup on code.
  13.  */
  14.  
  15. #include        <stdio.h>
  16. #include        "ed.h"
  17.  
  18. #define WFDEBUG 0                       /* Window flag debug. */
  19.  
  20. typedef struct  VIDEO {
  21.         short   v_flag;                 /* Flags */
  22.         char    v_text[1];              /* Screen data. */
  23. }       VIDEO;
  24.  
  25. #define VFCHG   0x0001                  /* Changed. */
  26.  
  27. int     sgarbf  = TRUE;                 /* TRUE if screen is garbage */
  28. int     mpresf  = FALSE;                /* TRUE if message in last line */
  29. int     vtrow   = 0;                    /* Row location of SW cursor */
  30. int     vtcol   = 0;                    /* Column location of SW cursor */
  31. int     ttrow   = HUGE;                 /* Row location of HW cursor */
  32. int     ttcol   = HUGE;                 /* Column location of HW cursor */
  33.  
  34. VIDEO   **vscreen;                      /* Virtual screen. */
  35. VIDEO   **pscreen;                      /* Physical screen. */
  36.  
  37. /*
  38.  * Initialize the data structures used by the display code. The edge vectors
  39.  * used to access the screens are set up. The operating system's terminal I/O
  40.  * channel is set up. All the other things get initialized at compile time.
  41.  * The original window has "WFCHG" set, so that it will get completely
  42.  * redrawn on the first call to "update".
  43.  */
  44. vtinit()
  45. {
  46.     register int i;
  47.     register VIDEO *vp;
  48.  
  49.     (*term.t_open)();
  50.     vscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
  51.  
  52.     if (vscreen == NULL)
  53.         exit(1);
  54.  
  55.     pscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
  56.  
  57.     if (pscreen == NULL)
  58.         exit(1);
  59.  
  60.     for (i = 0; i < term.t_nrow; ++i)
  61.         {
  62.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  63.  
  64.         if (vp == NULL)
  65.             exit(1);
  66.  
  67.         vscreen[i] = vp;
  68.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  69.  
  70.         if (vp == NULL)
  71.             exit(1);
  72.  
  73.         pscreen[i] = vp;
  74.         }
  75. }
  76.  
  77. /*
  78.  * Clean up the virtual terminal system, in anticipation for a return to the
  79.  * operating system. Move down to the last line and clear it out (the next
  80.  * system prompt will be written in the line). Shut down the channel to the
  81.  * terminal.
  82.  */
  83. vttidy()
  84. {
  85.     movecursor(term.t_nrow, 0);
  86.     (*term.t_eeol)();
  87.     (*term.t_close)();
  88. }
  89.  
  90. /*
  91.  * Set the virtual cursor to the specified row and column on the virtual
  92.  * screen. There is no checking for nonsense values; this might be a good
  93.  * idea during the early stages.
  94.  */
  95. vtmove(row, col)
  96. {
  97.     vtrow = row;
  98.     vtcol = col;
  99. }
  100.  
  101. /*
  102.  * Write a character to the virtual screen. The virtual row and column are
  103.  * updated. If the line is too long put a "$" in the last column. This routine
  104.  * only puts printing characters into the virtual terminal buffers. Only
  105.  * column overflow is checked.
  106.  */
  107. vtputc(c)
  108.     int c;
  109. {
  110.     register VIDEO      *vp;
  111.  
  112.     vp = vscreen[vtrow];
  113.  
  114.     if (vtcol >= term.t_ncol)
  115.         vp->v_text[term.t_ncol - 1] = '$';
  116.     else if (c == '\t')
  117.         {
  118.         do
  119.             {
  120.             vtputc(' ');
  121.             }
  122.         while ((vtcol&0x07) != 0);
  123.         }
  124.     else if (c < 0x20 || c == 0x7F)
  125.         {
  126.         vtputc('^');
  127.         vtputc(c ^ 0x40);
  128.         }
  129.     else
  130.         vp->v_text[vtcol++] = c;                
  131. }
  132.  
  133. /*
  134.  * Erase from the end of the software cursor to the end of the line on which
  135.  * the software cursor is located.
  136.  */
  137. vteeol()
  138. {
  139.     register VIDEO      *vp;
  140.  
  141.     vp = vscreen[vtrow];
  142.     while (vtcol < term.t_ncol)
  143.         vp->v_text[vtcol++] = ' ';
  144. }
  145.  
  146. /*
  147.  * Make sure that the display is right. This is a three part process. First,
  148.  * scan through all of the windows looking for dirty ones. Check the framing,
  149.  * and refresh the screen. Second, make sure that "currow" and "curcol" are
  150.  * correct for the current window. Third, make the virtual and physical
  151.  * screens the same.
  152.  */
  153. update()
  154. {
  155.     register LINE *lp;
  156.     register WINDOW *wp;
  157.     register VIDEO *vp1;
  158.     register VIDEO *vp2;
  159.     register int i;
  160.     register int j;
  161.     register int c;
  162.  
  163.     wp = wheadp;
  164.  
  165.     while (wp != NULL)
  166.         {
  167.         /* Look at any window with update flags set on. */
  168.  
  169.         if (wp->w_flag != 0)
  170.             {
  171.             /* If not force reframe, check the framing. */
  172.  
  173.             if ((wp->w_flag & WFFORCE) == 0)
  174.                 {
  175.                 lp = wp->w_linep;
  176.  
  177.                 for (i = 0; i < wp->w_ntrows; ++i)
  178.                     {
  179.                     if (lp == wp->w_dotp)
  180.                         goto out;
  181.  
  182.                     if (lp == wp->w_bufp->b_linep)
  183.                         break;
  184.  
  185.                     lp = lforw(lp);
  186.                     }
  187.                 }
  188.  
  189.             /* Not acceptable, better compute a new value for the line at the
  190.              * top of the window. Then set the "WFHARD" flag to force full
  191.              * redraw.
  192.              */
  193.             i = wp->w_force;
  194.  
  195.             if (i > 0)
  196.                 {
  197.                 --i;
  198.  
  199.                 if (i >= wp->w_ntrows)
  200.                   i = wp->w_ntrows-1;
  201.                 }
  202.             else if (i < 0)
  203.                 {
  204.                 i += wp->w_ntrows;
  205.  
  206.                 if (i < 0)
  207.                     i = 0;
  208.                 }
  209.             else
  210.                 i = wp->w_ntrows/2;
  211.  
  212.             lp = wp->w_dotp;
  213.  
  214.             while (i != 0 && lback(lp) != wp->w_bufp->b_linep)
  215.                 {
  216.                 --i;
  217.                 lp = lback(lp);
  218.                 }
  219.  
  220.             wp->w_linep = lp;
  221.             wp->w_flag |= WFHARD;       /* Force full. */
  222.  
  223. out:
  224.             /* Try to use reduced update. Mode line update has its own special
  225.              * flag. The fast update is used if the only thing to do is within
  226.              * the line editing.
  227.              */
  228.             lp = wp->w_linep;
  229.             i = wp->w_toprow;
  230.  
  231.             if ((wp->w_flag & ~WFMODE) == WFEDIT)
  232.                 {
  233.                 while (lp != wp->w_dotp)
  234.                     {
  235.                     ++i;
  236.                     lp = lforw(lp);
  237.                     }
  238.  
  239.                 vscreen[i]->v_flag |= VFCHG;
  240.                 vtmove(i, 0);
  241.  
  242.                 for (j = 0; j < llength(lp); ++j)
  243.                     vtputc(lgetc(lp, j));
  244.  
  245.                 vteeol();
  246.                 }
  247.              else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0)
  248.                 {
  249.                 while (i < wp->w_toprow+wp->w_ntrows)
  250.                     {
  251.                     vscreen[i]->v_flag |= VFCHG;
  252.                     vtmove(i, 0);
  253.  
  254.                     if (lp != wp->w_bufp->b_linep)
  255.                         {
  256.                         for (j = 0; j < llength(lp); ++j)
  257.                             vtputc(lgetc(lp, j));
  258.  
  259.                         lp = lforw(lp);
  260.                         }
  261.  
  262.                     vteeol();
  263.                     ++i;
  264.                     }
  265.                 }
  266. #if ~WFDEBUG
  267.             if ((wp->w_flag&WFMODE) != 0)
  268.                 modeline(wp);
  269.  
  270.             wp->w_flag  = 0;
  271.             wp->w_force = 0;
  272. #endif
  273.             }           
  274. #if WFDEBUG
  275.         modeline(wp);
  276.         wp->w_flag =  0;
  277.         wp->w_force = 0;
  278. #endif
  279.         wp = wp->w_wndp;
  280.         }
  281.  
  282.     /* Always recompute the row and column number of the hardware cursor. This
  283.      * is the only update for simple moves.
  284.      */
  285.     lp = curwp->w_linep;
  286.     currow = curwp->w_toprow;
  287.  
  288.     while (lp != curwp->w_dotp)
  289.         {
  290.         ++currow;
  291.         lp = lforw(lp);
  292.         }
  293.  
  294.     curcol = 0;
  295.     i = 0;
  296.  
  297.     while (i < curwp->w_doto)
  298.         {
  299.         c = lgetc(lp, i++);
  300.  
  301.         if (c == '\t')
  302.             curcol |= 0x07;
  303.         else if (c < 0x20 || c == 0x7F)
  304.             ++curcol;
  305.  
  306.         ++curcol;
  307.         }
  308.  
  309.     if (curcol >= term.t_ncol)          /* Long line. */
  310.         curcol = term.t_ncol-1;
  311.  
  312.     /* Special hacking if the screen is garbage. Clear the hardware screen,
  313.      * and update your copy to agree with it. Set all the virtual screen
  314.      * change bits, to force a full update.
  315.      */
  316.     if (sgarbf != FALSE)
  317.         {
  318.         for (i = 0; i < term.t_nrow; ++i)
  319.             {
  320.             vscreen[i]->v_flag |= VFCHG;
  321.             vp1 = pscreen[i];
  322.             for (j = 0; j < term.t_ncol; ++j)
  323.                 vp1->v_text[j] = ' ';
  324.             }
  325.  
  326.         movecursor(0, 0);               /* Erase the screen. */
  327.         (*term.t_eeop)();
  328.         sgarbf = FALSE;                 /* Erase-page clears */
  329.         mpresf = FALSE;                 /* the message area. */
  330.         }
  331.  
  332.     /* Make sure that the physical and virtual displays agree. Unlike before,
  333.      * the "updateline" code is only called with a line that has been updated
  334.      * for sure.
  335.      */
  336.     for (i = 0; i < term.t_nrow; ++i)
  337.         {
  338.         vp1 = vscreen[i];
  339.  
  340.         if ((vp1->v_flag&VFCHG) != 0)
  341.             {
  342.             vp1->v_flag &= ~VFCHG;
  343.             vp2 = pscreen[i];
  344.             updateline(i, &vp1->v_text[0], &vp2->v_text[0]);
  345.             }
  346.         }
  347.  
  348.     /* Finally, update the hardware cursor and flush out buffers. */
  349.  
  350.     movecursor(currow, curcol);
  351.     (*term.t_flush)();
  352. }
  353.  
  354. /*
  355.  * Update a single line. This does not know how to use insert or delete
  356.  * character sequences; we are using VT52 functionality. Update the physical
  357.  * row and column variables. It does try an exploit erase to end of line. The
  358.  * RAINBOW version of this routine uses fast video.
  359.  */
  360. updateline(row, vline, pline)
  361.     char vline[];
  362.     char pline[];
  363. {
  364. #if RAINBOW
  365.     register char *cp1;
  366.     register char *cp2;
  367.     register int nch;
  368.  
  369.     cp1 = &vline[0];                    /* Use fast video. */
  370.     cp2 = &pline[0];
  371.     putline(row+1, 1, cp1);
  372.     nch = term.t_ncol;
  373.  
  374.     do
  375.         {
  376.         *cp2 = *cp1;
  377.         ++cp2;
  378.         ++cp1;
  379.         }
  380.     while (--nch);
  381. #else
  382.     register char *cp1;
  383.     register char *cp2;
  384.     register char *cp3;
  385.     register char *cp4;
  386.     register char *cp5;
  387.     register int nbflag;
  388.  
  389.     cp1 = &vline[0];                    /* Compute left match.  */
  390.     cp2 = &pline[0];
  391.  
  392.     while (cp1!=&vline[term.t_ncol] && cp1[0]==cp2[0])
  393.         {
  394.         ++cp1;
  395.         ++cp2;
  396.         }
  397.  
  398.     /* This can still happen, even though we only call this routine on changed
  399.      * lines. A hard update is always done when a line splits, a massive
  400.      * change is done, or a buffer is displayed twice. This optimizes out most
  401.      * of the excess updating. A lot of computes are used, but these tend to
  402.      * be hard operations that do a lot of update, so I don't really care.
  403.      */
  404.     if (cp1 == &vline[term.t_ncol])             /* All equal. */
  405.         return;
  406.  
  407.     nbflag = FALSE;
  408.     cp3 = &vline[term.t_ncol];          /* Compute right match. */
  409.     cp4 = &pline[term.t_ncol];
  410.  
  411.     while (cp3[-1] == cp4[-1])
  412.         {
  413.         --cp3;
  414.         --cp4;
  415.         if (cp3[0] != ' ')              /* Note if any nonblank */
  416.             nbflag = TRUE;              /* in right match. */
  417.         }
  418.  
  419.     cp5 = cp3;
  420.  
  421.     if (nbflag == FALSE)                /* Erase to EOL ? */
  422.         {
  423.         while (cp5!=cp1 && cp5[-1]==' ')
  424.             --cp5;
  425.  
  426.         if (cp3-cp5 <= 3)               /* Use only if erase is */
  427.             cp5 = cp3;                  /* fewer characters. */
  428.         }
  429.  
  430.     movecursor(row, cp1-&vline[0]);     /* Go to start of line. */
  431.  
  432.     while (cp1 != cp5)                  /* Ordinary. */
  433.         {
  434.         (*term.t_putchar)(*cp1);
  435.         ++ttcol;
  436.         *cp2++ = *cp1++;
  437.         }
  438.  
  439.     if (cp5 != cp3)                     /* Erase. */
  440.         {
  441.         (*term.t_eeol)();
  442.         while (cp1 != cp3)
  443.             *cp2++ = *cp1++;
  444.         }
  445. #endif
  446. }
  447.  
  448. /*
  449.  * Redisplay the mode line for the window pointed to by the "wp". This is the
  450.  * only routine that has any idea of how the modeline is formatted. You can
  451.  * change the modeline format by hacking at this routine. Called by "update"
  452.  * any time there is a dirty window.
  453.  */
  454. modeline(wp)
  455.     WINDOW *wp;
  456. {
  457.     register char *cp;
  458.     register int c;
  459.     register int n;
  460.     register BUFFER *bp;
  461.  
  462.     n = wp->w_toprow+wp->w_ntrows;              /* Location. */
  463.     vscreen[n]->v_flag |= VFCHG;                /* Redraw next time. */
  464.     vtmove(n, 0);                               /* Seek to right line. */
  465.     vtputc('-');
  466.     bp = wp->w_bufp;
  467.  
  468.     if ((bp->b_flag&BFCHG) != 0)                /* "*" if changed. */
  469.         vtputc('*');
  470.     else
  471.         vtputc('-');
  472.  
  473.     n  = 2;
  474.     cp = " MicroEMACS -- ";                     /* Buffer name. */
  475.  
  476.     while ((c = *cp++) != 0)
  477.         {
  478.         vtputc(c);
  479.         ++n;
  480.         }
  481.  
  482.     cp = &bp->b_bname[0];
  483.  
  484.     while ((c = *cp++) != 0)
  485.         {
  486.         vtputc(c);
  487.         ++n;
  488.         }
  489.  
  490.     vtputc(' ');
  491.     ++n;
  492.  
  493.     if (bp->b_fname[0] != 0)            /* File name. */
  494.         {
  495.         cp = "-- File: ";
  496.  
  497.         while ((c = *cp++) != 0)
  498.             {
  499.             vtputc(c);
  500.             ++n;
  501.             }
  502.  
  503.         cp = &bp->b_fname[0];
  504.  
  505.         while ((c = *cp++) != 0)
  506.             {
  507.             vtputc(c);
  508.             ++n;
  509.             }
  510.  
  511.         vtputc(' ');
  512.         ++n;
  513.         }
  514.  
  515. #if WFDEBUG
  516.     vtputc('-');
  517.     vtputc((wp->w_flag&WFMODE)!=0  ? 'M' : '-');
  518.     vtputc((wp->w_flag&WFHARD)!=0  ? 'H' : '-');
  519.     vtputc((wp->w_flag&WFEDIT)!=0  ? 'E' : '-');
  520.     vtputc((wp->w_flag&WFMOVE)!=0  ? 'V' : '-');
  521.     vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : '-');
  522.     n += 6;
  523. #endif
  524.  
  525.     while (n < term.t_ncol)             /* Pad to full width. */
  526.         {
  527.         vtputc('-');
  528.         ++n;
  529.         }
  530. }
  531.  
  532. /*
  533.  * Send a command to the terminal to move the hardware cursor to row "row"
  534.  * and column "col". The row and column arguments are origin 0. Optimize out
  535.  * random calls. Update "ttrow" and "ttcol".
  536.  */
  537. movecursor(row, col)
  538.     {
  539.     if (row!=ttrow || col!=ttcol)
  540.         {
  541.         ttrow = row;
  542.         ttcol = col;
  543.         (*term.t_move)(row, col);
  544.         }
  545.     }
  546.  
  547. /*
  548.  * Erase the message line. This is a special routine because the message line
  549.  * is not considered to be part of the virtual screen. It always works
  550.  * immediately; the terminal buffer is flushed via a call to the flusher.
  551.  */
  552. mlerase()
  553.     {
  554.     movecursor(term.t_nrow, 0);
  555.     (*term.t_eeol)();
  556.     (*term.t_flush)();
  557.     mpresf = FALSE;
  558.     }
  559.  
  560. /*
  561.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  562.  * ABORT. The ABORT status is returned if the user bumps out of the question
  563.  * with a ^G. Used any time a confirmation is required.
  564.  */
  565. mlyesno(prompt)
  566.     char *prompt;
  567.     {
  568.     register int s;
  569.     char buf[64];
  570.  
  571.     for (;;)
  572.         {
  573.         strcpy(buf, prompt);
  574.         strcat(buf, " [y/n]? ");
  575.         s = mlreply(buf, buf, sizeof(buf));
  576.  
  577.         if (s == ABORT)
  578.             return (ABORT);
  579.  
  580.         if (s != FALSE)
  581.             {
  582.             if (buf[0]=='y' || buf[0]=='Y')
  583.                 return (TRUE);
  584.  
  585.             if (buf[0]=='n' || buf[0]=='N')
  586.                 return (FALSE);
  587.             }
  588.         }
  589.     }
  590.  
  591. /*
  592.  * Write a prompt into the message line, then read back a response. Keep
  593.  * track of the physical position of the cursor. If we are in a keyboard
  594.  * macro throw the prompt away, and return the remembered response. This
  595.  * lets macros run at full speed. The reply is always terminated by a carriage
  596.  * return. Handle erase, kill, and abort keys.
  597.  */
  598. mlreply(prompt, buf, nbuf)
  599.     char *prompt;
  600.     char *buf;
  601.     {
  602.     register int cpos;
  603.     register int i;
  604.     register int c;
  605.  
  606.     cpos = 0;
  607.  
  608.     if (kbdmop != NULL)
  609.         {
  610.         while ((c = *kbdmop++) != '\0')
  611.             buf[cpos++] = c;
  612.  
  613.         buf[cpos] = 0;
  614.  
  615.         if (buf[0] == 0)
  616.             return (FALSE);
  617.  
  618.         return (TRUE);
  619.         }
  620.  
  621.     mlwrite(prompt);
  622.  
  623.     for (;;)
  624.         {
  625.         c = (*term.t_getchar)();
  626.  
  627.         switch (c)
  628.             {
  629.             case 0x0D:                  /* Return, end of line */
  630.                 buf[cpos++] = 0;
  631.  
  632.                 if (kbdmip != NULL)
  633.                     {
  634.                     if (kbdmip+cpos > &kbdm[NKBDM-3])
  635.                         {
  636.                         ctrlg(FALSE, 0);
  637.                         (*term.t_flush)();
  638.                         return (ABORT);
  639.                         }
  640.  
  641.                     for (i=0; i<cpos; ++i)
  642.                         *kbdmip++ = buf[i];
  643.                     }
  644.  
  645.                 (*term.t_putchar)('\r');
  646.                 ttcol = 0;
  647.                 (*term.t_flush)();
  648.  
  649.                 if (buf[0] == 0)
  650.                     return (FALSE);
  651.  
  652.                 return (TRUE);
  653.  
  654.             case 0x07:                  /* Bell, abort */
  655.                 (*term.t_putchar)('^');
  656.                 (*term.t_putchar)('G');
  657.                 ttcol += 2;
  658.                 ctrlg(FALSE, 0);
  659.                 (*term.t_flush)();
  660.                 return (ABORT);
  661.  
  662.             case 0x7F:                  /* Rubout, erase */
  663.             case 0x08:                  /* Backspace, erase */
  664.                 if (cpos != 0)
  665.                     {
  666.                     (*term.t_putchar)('\b');
  667.                     (*term.t_putchar)(' ');
  668.                     (*term.t_putchar)('\b');
  669.                     --ttcol;
  670.  
  671.                     if (buf[--cpos] < 0x20)
  672.                         {
  673.                         (*term.t_putchar)('\b');
  674.                         (*term.t_putchar)(' ');
  675.                         (*term.t_putchar)('\b');
  676.                         --ttcol;
  677.                         }
  678.  
  679.                     (*term.t_flush)();
  680.                     }
  681.  
  682.                 break;
  683.  
  684.             case 0x15:                  /* C-U, kill */
  685.                 while (cpos != 0)
  686.                     {
  687.                     (*term.t_putchar)('\b');
  688.                     (*term.t_putchar)(' ');
  689.                     (*term.t_putchar)('\b');
  690.                     --ttcol;
  691.  
  692.                     if (buf[--cpos] < 0x20)
  693.                         {
  694.                         (*term.t_putchar)('\b');
  695.                         (*term.t_putchar)(' ');
  696.                         (*term.t_putchar)('\b');
  697.                         --ttcol;
  698.                         }
  699.                     }
  700.  
  701.                 (*term.t_flush)();
  702.                 break;
  703.  
  704.             default:
  705.                 if (cpos < nbuf-1)
  706.                     {
  707.                     buf[cpos++] = c;
  708.  
  709.                     if (c < ' ')
  710.                         {
  711.                         (*term.t_putchar)('^');
  712.                         ++ttcol;
  713.                         c ^= 0x40;
  714.                         }
  715.  
  716.                     (*term.t_putchar)(c);
  717.                     ++ttcol;
  718.                     (*term.t_flush)();
  719.                     }
  720.             }
  721.         }
  722.     }
  723.  
  724. /*
  725.  * Write a message into the message line. Keep track of the physical cursor
  726.  * position. A small class of printf like format items is handled. Assumes the
  727.  * stack grows down; this assumption is made by the "++" in the argument scan
  728.  * loop. Set the "message line" flag TRUE.
  729.  */
  730. mlwrite(fmt, arg)
  731.     char *fmt;
  732.     {
  733.     register int c;
  734.     register char *ap;
  735.  
  736.     movecursor(term.t_nrow, 0);
  737.     ap = (char *) &arg;
  738.     while ((c = *fmt++) != 0) {
  739.         if (c != '%') {
  740.             (*term.t_putchar)(c);
  741.             ++ttcol;
  742.             }
  743.         else
  744.             {
  745.             c = *fmt++;
  746.             switch (c) {
  747.                 case 'd':
  748.                     mlputi(*(int *)ap, 10);
  749.                     ap += sizeof(int);
  750.                     break;
  751.  
  752.                 case 'o':
  753.                     mlputi(*(int *)ap,  8);
  754.                     ap += sizeof(int);
  755.                     break;
  756.  
  757.                 case 'x':
  758.                     mlputi(*(int *)ap, 16);
  759.                     ap += sizeof(int);
  760.                     break;
  761.  
  762.                 case 'D':
  763.                     mlputli(*(long *)ap, 10);
  764.                     ap += sizeof(long);
  765.                     break;
  766.  
  767.                 case 's':
  768.                     mlputs(*(char **)ap);
  769.                     ap += sizeof(char *);
  770.                     break;
  771.  
  772.                 default:
  773.                     (*term.t_putchar)(c);
  774.                     ++ttcol;
  775.                 }
  776.             }
  777.         }
  778.     (*term.t_eeol)();
  779.     (*term.t_flush)();
  780.     mpresf = TRUE;
  781.     }
  782.  
  783. /*
  784.  * Write out a string. Update the physical cursor position. This assumes that
  785.  * the characters in the string all have width "1"; if this is not the case
  786.  * things will get screwed up a little.
  787.  */
  788. mlputs(s)
  789.     char *s;
  790.     {
  791.     register int c;
  792.  
  793.     while ((c = *s++) != 0)
  794.         {
  795.         (*term.t_putchar)(c);
  796.         ++ttcol;
  797.         }
  798.     }
  799.  
  800. /*
  801.  * Write out an integer, in the specified radix. Update the physical cursor
  802.  * position. This will not handle any negative numbers; maybe it should.
  803.  */
  804. mlputi(i, r)
  805.     {
  806.     register int q;
  807.     static char hexdigits[] = "0123456789ABCDEF";
  808.  
  809.     if (i < 0)
  810.         {
  811.         i = -i;
  812.         (*term.t_putchar)('-');
  813.         }
  814.  
  815.     q = i/r;
  816.  
  817.     if (q != 0)
  818.         mlputi(q, r);
  819.  
  820.     (*term.t_putchar)(hexdigits[i%r]);
  821.     ++ttcol;
  822.     }
  823.  
  824. /*
  825.  * do the same except as a long integer.
  826.  */
  827. mlputli(l, r)
  828.     long l;
  829.     {
  830.     register long q;
  831.  
  832.     if (l < 0)
  833.         {
  834.         l = -l;
  835.         (*term.t_putchar)('-');
  836.         }
  837.  
  838.     q = l/r;
  839.  
  840.     if (q != 0)
  841.         mlputli(q, r);
  842.  
  843.     (*term.t_putchar)((int)(l%r)+'0');
  844.     ++ttcol;
  845.     }
  846.  
  847. #if RAINBOW
  848.  
  849. putline(row, col, buf)
  850.     int row, col;
  851.     char buf[];
  852.     {
  853.     int n;
  854.  
  855.     n = strlen(buf);
  856.     if (col + n - 1 > term.t_ncol)
  857.         n = term.t_ncol - col + 1;
  858.     Put_Data(row, col, n, buf);
  859.     }
  860. #endif
  861.  
  862.